<?php

 /**
  * @package JooS
  * @subpackage PHPackager
  */

  require_once "JooS/Helper/Subject.php";

  abstract class JooS_PHPackager_Instance implements JooS_Helper_Subject {
    const VARIABLE_FILES = "Files";

    protected $call = null;

    /**
     * @var JooS_Namespace
     */
    private $_ns = null;

    /**
     * @var JooS_Helper_Broker
     */
    private $_helperBroker = null;

    /**
     * @var string
     */
    protected static $_name = null;

    /**
     * @var string
     */
    protected static $_version = null;

    /**
     * @var string
     */
    protected static $_control = null;

    /**
     * @var array
     */
    private $_depends;
    
    /**
     * @var array
     */
    private $_conflicts;

    /**
     * @var array
     */
    private $_breaks;
    
    /**
     * @var array
     */
    private $_provides;

    /**
     * @var array
     */
    private $_replaces;

    /**
     * @var JooS_PHPackager_Archive
     */
    private $_archive = null;

    /**
     * @var string
     */
    private $_installPath = null;

    public function __construct() {
      require_once "JooS/Config.php";
      require_once "JooS/Namespace.php";

      $this->_ns = new JooS_Namespace( $this->getConfig( $this->getEnvironmentConfigName() )->valueOf() );
      $this->_call = $this->helperBroker()->PHPackager_Helper_Call;

      $this->_depends = $this->_initDepends();
      $this->_conflicts = $this->_initConflicts();
      $this->_breaks = $this->_initBreaks();
      $this->_provides = $this->_initProvides();
      $this->_replaces = $this->_initReplaces();
    }

    /**
     * @param string $stage
     * @param string $old_version
     */
    public function add($stage, $old_version = null) {
      require_once "JooS/PHPackager/FS.php";

      $Event_newFile = JooS_Event::newInstance(JooS_PHPackager_FS::EVENT_NEW_FILE)
        ->attach(array($this, "Observer_addFile"))
        ->commit();

      $this->_ns->nsPush();
      $this->setVariable($this->getVariable("version"), $old_version);
      $this->_add($stage);
      $this->_ns->nsPop();

      $Event_newFile->rollback();
    }

    /**
     * @param string $stage
     * @param string $new_version
     */
    public function delete($stage, $new_version = null) {
      require_once "JooS/PHPackager/FS.php";

      $Event_deleteFile = JooS_Event::newInstance(JooS_PHPackager_FS::EVENT_DELETE_FILE)
        ->attach(array($this, "Observer_deleteFile"))
        ->commit();

      $this->_ns->nsPush();
      $this->setVariable($this->getVariable("version"), $new_version);
      $this->_delete($stage);
      $this->_ns->nsPop();

      $Event_deleteFile->rollback();
    }

    /**
     * @return string
     */
    public function getName() {
      return static::$_name;
    }

    /**
     * @return string
     */
    public function getVersion() {
      return static::$_version;
    }

    /**
     * @return string
     */
    public function getControlXML() {
      return static::$_control;
    }

    /**
     * @return JooS_PHPackager_Instance
     */
    public function createArchive() {
      require_once "JooS/PHPackager/Archive.php";

      $this->_archive = new JooS_PHPackager_Archive();
      $this->_archive
        ->create()
        ->addFromString("phpackage.xml", $this->getControlXML());

      return $this;
    }

    /**
     * @return JooS_PHPackager_Archive
     */
    public function getArchive() {
      return $this->_archive;
    }

    /**
     * @param string $path
     * @return JooS_PHPackager_Instance
     */
    public function setInstallPath($path) {
      $this->_installPath = $path;

      return $this;
    }

    /**
     * @return string
     */
    public function getInstallPath() {
      return $this->_installPath;
    }    

    /**
     * @return array
     */
    public function getDepends() {
      return $this->_depends;
    }

    /**
     * @return array
     */
    public function getConflicts() {
      return $this->_conflicts;
    }

    /**
     * @return array
     */
    public function getBreaks() {
      return $this->_breaks;
    }

    /**
     * @return array
     */
    public function getProvides() {
      return $this->_provides;
    }

    /**
     * @return array
     */
    public function getReplaces() {
      return $this->_replaces;
    }

    /**
     * @return array
     */
    public function &getEnvironment() {
      return $this->getRootVariable("");
    }

    /**
     * @return string
     */
    public function getEnvironmentConfigName() {
      return implode("_", array(
        "PHPackager", 
        "Environment", 
        $this->getName(), 
        $this->getVersion(), 
      ));
    }

    /**
     * @param string $path
     * @return mixed
     */
    protected function &getVariable($path) {
      return $this->_ns->get($path);
    }

    /**
     * @param int $times
     * @param string $path
     * @return mixed
     */
    protected function &getBackVariable($times, $path) {
      return $this->_ns->getBack($times, $path);
    }

    /**
     * @param string $path
     * @return array
     */
    protected function &getRootVariable($path) {
      return $this->_ns->getRoot($path);
    }

    /**
     * @param mixed $get
     * @param mixed $value
     */
    protected function setVariable(&$get, $value) {
      $this->_ns->set($get, $value);
    }

    /**
     * @param string $name
     * @return JooS_Config
     */
    protected function getConfig($name) {
      require_once "JooS/Config.php";

      return JooS_Config::getInstance($name);
    }

    /**
     * @param string $name
     * @return JooS_PHPackager_Relation_Package
     */
    protected function getRelationByName($name) {
      require_once "JooS/PHPackager/Relation/Package.php";

      return JooS_PHPackager_Relation_Package::newInstance()
        ->setName($name);
    }

    /**
     * @param string $name
     * @param string $operator
     * @param string $version
     * @return JooS_PHPackager_Relation_Package
     */
    protected function getRelationByVersion($name, $operator, $version) {
      return $this->getRelationByName($name)
        ->setOperator($operator)
        ->setVersion($version);
    }

    /**
     * @param string $name
     * @return JooS_PHPackager_Relation_Extension
     */
    protected function getRelationByExtension($name) {
      require_once "JooS/PHPackager/Relation/Extension.php";

      return JooS_PHPackager_Relation_Extension::newInstance()
        ->setName($name);
    }

    /**
     * @param string $name
     * @param string $value
     * @return JooS_PHPackager_Relation_IniGet
     */
    protected function getRelationByIniGet($name, $value) {
      require_once "JooS/PHPackager/Relation/IniGet.php";

      return JooS_PHPackager_Relation_IniGet::newInstance()
        ->setName($name)
        ->setValue($value);
    }

    /**
     * @return array
     */
    private function &getFilesNS() {
      return $this->getRootVariable(self::VARIABLE_FILES);
    }

    /**
     * @return array
     */
    public function getFiles() {
      return $this->getFilesNS();
    }

    /**
     * @return int
     */
    public function countFiles() {
      $files = $this->getFilesNS();
      return is_array($files) ? sizeof($files) : 0;
    }

    /**
     * @param string $realPath
     * @param string $path
     */
    public function Observer_addFile($realPath, $path) {
      $archive = $this->getArchive();
      if ($archive) {
        $tmpRoot = JooS_PHPackager_FS::getInstance()->_getNormalizedPath( $this->getInstallPath() );
        $localName = substr($realPath, strlen($tmpRoot) + 1);

        $archive->addFile($realPath, $localName);
      }

      $files = $this->getFilesNS();
      if (!is_array($files))
        $files = array();
      array_push($files, $path);

      $this->setVariable($this->getFilesNS(), $files);
    }

    /**
     * @param string $realPath
     */
    public function Observer_deleteFile($realPath) {
      $pkgRoot = JooS_PHPackager_FS::getInstance()->_getNormalizedPath(
        JooS_Config::PHPackager()->path()
      );
      $localName = substr($realPath, strlen($pkgRoot) + 1);

      $files = $this->getFilesNS();
      if (!is_array($files))
        $files = array();

      $key = array_search($localName, $files);
      if ($key === null)
        self::throwException("Trying to delete unfamiliar file " . $this->getName() . " v" . $this->getVersion() . " ($localName)");

      $this->setVariable($this->getFilesNS(), array_values($files));
    }

    /**
     * @return JooS_Helper_Broker
     */
    public function helperBroker() {
      if ($this->_helperBroker === null) {
        require_once "JooS/Helper/Broker.php";

        $this->_helperBroker = JooS_Helper_Broker::newInstance($this);
      }
      return $this->_helperBroker;
    }

    /**
     * @param string $stage
     */
    protected function _add($stage) {
    }

    /**
     * @param string $stage
     */
    protected function _delete($stage) {
    }

    /**
     * @return array
     */
    protected function _initDepends() {
      return array();
    }

    /**
     * @return array
     */
    protected function _initConflicts() {
      return array();
    }

    /**
     * @return array
     */
    protected function _initBreaks() {
      return array();
    }

    /**
     * @return array
     */
    protected function _initProvides() {
      return array();
    }

    /**
     * @return array
     */
    protected function _initReplaces() {
      return array();
    }

    /**
     * @param string $Message
     * @param int $Code
     * @throws JooS_PHPackager_Instance_Exception
     */
    protected static function throwException($Message, $Code = 0) {
      require_once "JooS/PHPackager/Instance/Exception.php";

      throw new JooS_PHPackager_Instance_Exception($Message, $Code);
    }
  }
